use std::collections::HashMap;
use std::io::{Read, Write};
use std::net::TcpStream;
use std::borrow::Borrow;
use crate::log::Logger;


#[derive(Debug)]
pub enum HttpVersion {
    HttP11,
    HTTP10,
    UNKNOWN,
}

#[derive(Debug, Clone)]
pub enum HttpMethod {
    Get,
    Post,
    Delete,
    UNKNOWN,
}

pub struct Router {
    path: String,
    handler: fn(),
}

#[derive(Debug)]
pub enum HttpStatus {
    OK,
    Redirect,
    NotFound,
    Forbidden,
}


#[derive(Debug)]
pub struct HttpRequest {
    uri: String,
    method: HttpMethod,
    http_version: HttpVersion,
    params: HashMap<String, String>,
    request_body: String,
}

#[derive(Debug)]
pub enum ContentType {
    TextHtml,
    ImageJpeg,
    TextJavascript,
    TextCss,
    ApplicationJson,
    TextPlain,
}

#[derive(Debug)]
pub struct HttpResponse {
    response_body: Vec<u8>,
    response_headers: HashMap<String, String>,
    http_version: HttpVersion,
}

impl Default for HttpResponse {
    fn default() -> Self {
        HttpResponse { response_body: vec![0], response_headers: HashMap::new(), http_version: HttpVersion::UNKNOWN }
    }
}


pub trait Serializable {
    fn serialize(&self) -> String;
}

impl Default for HttpRequest {
    fn default() -> Self {
        HttpRequest { uri: String::from("none"), method: HttpMethod::UNKNOWN, http_version: HttpVersion::UNKNOWN, params: HashMap::new(), request_body: "".to_string() }
    }
}

impl HttpResponse {
    pub fn new() -> Self {
        HttpResponse {
            response_headers: HashMap::new(),
            response_body: Vec::new(),
            http_version: HttpVersion::HttP11,
        }
    }

    pub fn new_with_buf(buf: Vec<u8>) -> Self {
        HttpResponse {
            response_body: buf,
            response_headers: HashMap::new(),
            http_version: HttpVersion::HttP11,
        }
    }

    pub fn new_with_buf_version(buf: Vec<u8>, version: HttpVersion) -> Self {
        HttpResponse {
            response_body: buf,
            response_headers: HashMap::new(),
            http_version: version,
        }
    }

    pub fn set_header(&mut self, key: String, value: String) -> &mut Self {
        self.response_headers.insert(key, value);
        self
    }

    pub fn allowed_cross_origin(&mut self) -> &mut Self {
        let headers = &mut self.response_headers;
        headers.insert("Access-Control-Allow-Origin".to_string(), "*".to_string());
        headers.insert("Access-Control-Allow-Methods".to_string(), "GET, POST, PUT, DELETE, OPTION".to_string());
        headers.insert("Access-Control-Allow-Headers".to_string(), "*".to_string());
        headers.insert("Access-Control-Allow-Credentials".to_string(), "true".to_string());
        self
    }

    pub fn set_content_type(&mut self, content_type: ContentType) -> &mut Self {
        let value = match content_type {
            ContentType::TextHtml => "text/html".to_string(),
            ContentType::ImageJpeg => "image/jpeg".to_string(),
            ContentType::TextJavascript => "text/javascript".to_string(),
            ContentType::TextCss => "text/css".to_string(),
            ContentType::ApplicationJson => "application/json".to_string(),
            ContentType::TextPlain => "text/plain".to_string()
        };
        self.set_header("Content-Type".to_string(), value);
        self
    }

    fn create_response_header(&self, status: HttpStatus) -> Vec<u8> {
        let mut headers: String = match self.http_version {
            HttpVersion::HttP11 => "HTTP/1.1 ".to_string(),
            HttpVersion::HTTP10 => "HTTP/1.0 ".to_string(),
            HttpVersion::UNKNOWN => "HTTP/1.1".to_string(),
        };
        headers += match status {
            HttpStatus::OK => "200 OK\r\n",
            HttpStatus::Redirect => "300 Redirection\r\n",
            HttpStatus::NotFound => "404 Not Found\r\n",
            HttpStatus::Forbidden => "403 Forbidden\r\n"
        };
        for (key, value) in &self.response_headers {
            headers += key.as_str();
            headers += ": ";
            headers += value.as_str();
            headers += "\r\n"
        }
        headers += "\r\n";
        headers.into_bytes()
    }

    pub fn write(&mut self, buf: Vec<u8>) -> &mut Self {
        self.response_body = buf;
        self.response_headers.insert("Content-Length".to_string(), self.response_body.len().to_string());
        self
    }

    pub fn write_obj(&mut self, body: &dyn Serializable) {
        self.response_body = body.serialize().as_bytes().to_owned();
        self.response_headers.insert("Content-Length".to_string(), self.response_body.len().to_string());
        self.response_headers.insert("Content-Type".to_string(), "application/json".to_string());
    }

    pub fn send(&self, mut stream: &TcpStream, status: HttpStatus) {
        let mut headers = self.create_response_header(status);
        headers.extend(self.response_body.clone());
        stream.write(headers.borrow()).unwrap();
        stream.flush().unwrap();
    }
}

impl HttpRequest {
    pub fn parse_param(uri: String) -> HashMap<String, String> {
        let ps: Vec<_> = uri.split('&').collect();
        let mut map: HashMap<String, String> = HashMap::new();
        for i in &ps {
            let tmp: Vec<_> = i.split('=').collect();
            map.insert(tmp[0].to_string(), tmp[1].to_string());
        }
        map
    }

    pub fn get_body(&self) -> String {
        dbg!(&self.request_body);
        self.request_body.clone()
    }

    pub fn new(stream: &mut TcpStream) -> Result<Self, String> {
        let mut buf: [u8; 1024] = [0; 1024];
        let mut http_request: HttpRequest = Default::default();
        let len = stream.read(&mut buf);
        let len = match len {
            Ok(len) => {
                if len == 0 {
                    return Result::Err("have no data".to_string());
                } else {
                    len
                }
            }
            Err(_) => return Result::Err("have no data".to_string())
        };
        if let Ok(res) = String::from_utf8(buf[0..len].to_owned()) {
            let words: Vec<_> = res.split(' ').collect();
            http_request.method = match words[0] {
                "GET" => HttpMethod::Get,
                "POST" => HttpMethod::Post,
                "DELETE" => HttpMethod::Delete,
                _ => HttpMethod::Get
            };
            let tmp = String::from(words[1]);
            let tmps: Vec<_> = tmp.split('?').collect();
            http_request.uri = String::from(tmps[0]);

            if let Some(t) = tmps.get(1) {
                let map = Self::parse_param(String::from(*t));
                println!("map is : {:?}", map);
                http_request.params = map;
            }

            let version: String = words[2].to_string();
            let version: Vec<_> = version.split("\r\n").collect();
            http_request.http_version = match version[0] {
                "HTTP/1.1" => HttpVersion::HttP11,
                "HTTP/1.0" => HttpVersion::HTTP10,
                _ => HttpVersion::UNKNOWN
            };
            let b: Vec<_> = res.split("\r\n\r\n").collect();
            if let Some(t) = b.get(1) {
                http_request.request_body = t.to_string();
            }
        } else {
            Logger::err("request failed...")
        }
        Result::Ok(http_request)
    }

    pub fn get_uri(&self) -> &String {
        &self.uri
    }

    pub fn get_method(&self) -> &HttpMethod {
        &self.method
    }

    pub fn get_version(&self) -> &HttpVersion {
        &self.http_version
    }
}